/**
 * ajax 基于 axios 的封装
 * @Author Canaan
 * @Date 2018/6/6.
 */
import axios from 'axios';
import Qs from 'qs';

const install = function (Vue) {
    const _vue = new Vue();
    const cancelTokenSource = axios.CancelToken.source();
    const Instance = {
        semaphore: undefined,
        lockError: 'Idea-755a90e2=09253061-faaf-46f5-b8c4-b8c976bf1609',
        // timeout: 1000,          //指定请求超时的毫秒数
        // headers: {'X-Custom-Header': 'foobar'},  //
        // `validateStatus` 定义对于给定的HTTP 响应状态码是 resolve 或 reject  promise 。如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`)，promise 将被 resolve; 否则，promise 将被 rejecte
        // validateStatus: function (status) {
        //     return status >= 200 && status < 300; // 默认的
        // },

        // paramsSerializer 是一个负责 `params` 序列化的函数
        // responseType 表示服务器响应的数据类型，可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
        // xsrfCookieName    是用作 xsrf token 的值的cookie的名称
        // xsrfHeaderName  是承载 xsrf token 的值的 HTTP 头的名称
        // onDownloadProgress  允许为下载处理进度事件
        // maxContentLength  定义允许的响应内容的最大尺寸
    };

    /**
     * 表单提交 - 加锁
     * @return boolean true  获取锁成功 ，false 获取锁失败
     */
    Instance.tryLock = function () {
        if (Instance.semaphore) {
            return false;
        }

        Instance.semaphore = Math.uuid();
        //加锁最长5秒，将自动释放锁
        setTimeout(() => {
            Instance.unLock();
        }, 5000);
        return true;
    };

    /**
     * 表单提交 - 解锁
     */
    Instance.unLock = function () {
        Instance.semaphore = undefined;
    };

    /**
     * ajax 通用配置
     */
    Instance.ajax = axios.create({
        baseURL: '/tasty_mgr',
        timeout: 7000,
        headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json',
            'Authorization': 'Bearer'
        },
        paramsSerializer(params) {
            //【indices】  【brackets】  【repeat】
            return Qs.stringify(params, {arrayFormat: 'repeat'})
        },
        // validateStatus: Instance.validateStatus
    });
    Instance.ajax.$constant = {
        notifyMsg: 'notify-msg' //是不通知消息(boolean)
    };

    /**
     *  添加请求拦截器
     */
    Instance.ajax.interceptors.request.use(function (config) {
        // 在发送请求之前做些什么
        config.headers.Authorization = 'Bearer ' + sessionStorage.getItem(_vue.$env.LOGIN_TOKEN);
        if (config.method === 'get') {
            return config;
        }

        //如果不是get提交，那么就要加锁，防止表单提交
        if (!Instance.tryLock()) {
            //获取锁失败撤销提交
            config.cancelToken = cancelTokenSource.token;
            cancelTokenSource.cancel(Instance.lockError);
        }

        return config;
    }, function (error) {
        Instance.unLock();
        // 对请求错误做些什么
        return Promise.reject(error);
    });

    /**
     * 二进制流处理，用于ajax文件下载
     */
    function blobDataHandle(res) {
        let blobType = res.data.type || "";
        //excel文档下载
        if (blobType.indexOf("application/vnd.ms-excel") >= 0) {
            const blob = new Blob([res.data], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8'}); //application/vnd.openxmlformats-officedocument.spreadsheetml.sheet这里表示xlsx类型
            let filename = 'excel.xls';
            if (res.headers.filename) {
                filename = decodeURIComponent(res.headers.filename);
            }

            if ('download' in document.createElement('a')) {
                const downloadElement = document.createElement('a');
                let href = '';
                if (window.URL) href = window.URL.createObjectURL(blob);
                else href = window.webkitURL.createObjectURL(blob);
                downloadElement.href = href;
                downloadElement.download = filename;
                document.body.appendChild(downloadElement);
                downloadElement.click();
                if (window.URL) window.URL.revokeObjectURL(href);
                else window.webkitURL.revokeObjectURL(href);
                document.body.removeChild(downloadElement);
            } else {
                navigator.msSaveBlob(blob, filename);
            }
            return;
        }

        throw "示处理的二进制：" + blobType;
    }

    /**
     * 添加响应拦截器
     */
    Instance.ajax.interceptors.response.use(res => {
        Instance.unLock();

        //二进制流处理
        if (res.config && res.config.responseType === 'blob') {
            blobDataHandle(res);
            return;
        }

        //get 请求不处理
        if (res.config.method === 'get') {
            return res.data;
        }

        //这里只处理成功的消息
        if (res.data.code === "ok" || res.data.code === 0) {
            //不用显示消息
            if (res.config.headers[Instance.ajax.$constant.notifyMsg] === false) {
                return res.data;
            }

            if (res.data.msg && res.data.msg.length <= 10) {
              // _vue.$message.success(res.data.msg);
            } else if (res.data.msg) {
                // _vue.$message.success(res.data.msg);
            } else {
                // _vue.$message.success('成功？？？');
            }
        }
        return res.data;
    }, function (error) {
        Instance.unLock();

        if (!error.response) {
            if (Instance.lockError !== error.message) {
                _vue.$message.error("请求出错了：" + error.message);
            }
            return Promise.reject(error);
        }

        // 请求已发出，但服务器响应的状态码不在 2xx 范围内
        // if('Security.sessionTimeOut'===error.response.data.code){
        //     _vue.$bus.$emit(_vue.$env.TOKEN_INVALID);
        // }else
        if (error.response.status === 401) {
            _vue.$bus.$emit(_vue.$env.TOKEN_INVALID);
        } else if (error.response.status === 403) {
            _vue.$message.error("访问拒绝：" + error.response.data.msg);
        } else {
            _vue.$message.error("【" + error.response.status + "】请求出错了" + JSON.stringify(error.response.data));
        }
        return Promise.reject(error);

    });

    Vue.prototype.$ajax = Instance.ajax;    //封装后的
    Vue.prototype.$axios = axios;           //原生的
};

export default install;

